#coding=utf-8
#K-近邻算法（KNN）
from numpy import *    #科学计算包
import operator         #运算符模块，用来做排序等
import matplotlib       #绘图
import matplotlib.pyplot as plt
from os import listdir  #列出给定目录的文件名

#创建数据集和标签（已知特征值和对应标签的数据集）
def createrDataSet():
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])    #四组特征值
    labels = ['A','A','B','B']                          #四个标签
    return group,labels

'''
K-近邻算法：
（1）计算已知类别数据集中的点与当前点之间的距离
（2）按照距离递增次序排序
（3）选取与当前点距离最小的k个点
（4）确定前k个点所在类别的出现频率
（5）返回前k个点出现频率最高的类别作为当前点的预测分类
'''
def classify0(inX, dataSet, labels, k): #输入向量inX, 样本集dataSet， 标签向量labels， k最近邻居的数目
    dataSetSize = dataSet.shape[0]      #样本集中数据的个数

    #计算距离
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet     #numpy.tile(A,reps) #简单理解是此函数将A进行重复输出
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)     #axis=0 就是普通的相加 axis=1是将一个矩阵的每一行向量相加
    distances = sqDistances**0.5

    sortedDistIndicies = distances.argsort()    #按距离从小到大排列，提取其对应的index(索引)
    classCount={}
    #确定前k个点所在类别的出现次数
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1

    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    #print(sortedClassCount)
    return sortedClassCount[0][0]


#输入文件名，将文件转换为训练样本矩阵和类标签向量
def file2matrix(filename):
    fr = open(filename)
    arrayOfLines =  fr.readlines()
    numberOfLines = len(arrayOfLines)        #文件行数

    returnMat = zeros((numberOfLines, 3))   #创建返回的矩阵
    classLabelVector = []                   #创建返回的类标签向量

    index = 0
    for line in arrayOfLines:
        line = line.strip()         #去掉字符串头尾的空格
        listFromLine = line.split('\t')
        returnMat[index, :] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1

    return returnMat,classLabelVector

#归一化特征值,将特征值都处理为0-1之间的数 newValue = (oldValue - min)/(max - min)
def autoNorm(dataSet):
    minVals = dataSet.min(0)                # axis=0; 每列的最小值   axis=1；每行的最小值
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals

    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (m,1))
    normDataSet = normDataSet/tile(ranges, (m,1))

    return normDataSet,ranges,minVals

#分类器针对约会网站的测试代码
def datingClassTest():
    hoRatio = 0.10
    datingDataMat, datingLabels = file2matrix("/Users/xubobo/Desktop/毕业设计/书籍与论文/书籍/机器学习实战源码/Ch02/datingTestSet2.txt")
    normMat, ranges, minValues = autoNorm(datingDataMat)
    m = normMat.shape[0]
    numTestVecs = int(m*hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
        print("the classifier came back with: %d, the real answer is: %d" %(classifierResult, datingLabels[i]))
        if (classifierResult != datingLabels[i]):
            errorCount += 1

    print("the total error rate is %f" %(errorCount/float(numTestVecs)))

#约会网站预测函数
def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    percentTats = float(input("percentage of time spent playing video games?"))
    ffMiles = float(input("frequent flier miles earned per year?"))
    iceCream = float(input("liters of ice cream consumed per year?"))

    datingDataMat, datingLabels = file2matrix("/Users/xubobo/Desktop/毕业设计/书籍与论文/书籍/机器学习实战源码/Ch02/datingTestSet2.txt")                   
    normMat, ranges, minValues = autoNorm(datingDataMat)
    inArr = array([ffMiles, percentTats, iceCream])
    classifierResult = classify0((inArr - minValues)/ranges, normMat, datingLabels, 3)
    print("You will probably like this person:", resultList[classifierResult-1])


#将图像格式转换为一个向量 32*32的二进制图像矩阵转换为1*1024的向量
def img2vector(filename):
    returnVect = zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        linStr = fr.readline()
        for j in range(32):
            returnVect[0, 32*i+j] = int(linStr[j])

    return returnVect

#手写数字识别系统的测试代码
def handwritingClassTest():
    hwLabels = []
    dirName = "/Users/xubobo/Desktop/毕业设计/书籍与论文/书籍/机器学习实战源码/Ch02/digits/trainingDigits/"
    trainingFileList = listdir(dirName)
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)

        trainingMat[i,:] = img2vector('%s%s' % (dirName, fileNameStr))

    testFileDir = "/Users/xubobo/Desktop/毕业设计/书籍与论文/书籍/机器学习实战源码/Ch02/digits/testDigits"
    testFileList = listdir(testFileDir)
    errorCount = 0.0
    mTest = len(testFileList)
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector("%s/%s" % (testFileDir, fileNameStr))
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print("the classifier came back with: %d, the real answer is: %d" %(classifierResult, classNumStr))
        if (classifierResult != classNumStr):
            errorCount += 1

    print("the total number of errors is: %d" %errorCount)
    print("the total rate is: %f" % (errorCount/float(mTest)))


def main():
    handwritingClassTest()
    # classifyPerson()

    # group,labels = createrDataSet()
    # print(classify0([1.0,0.9], group, labels, 3))
    # datingDataMat, datingLabels = file2matrix("/Users/xubobo/Desktop/毕业设计/书籍与论文/书籍/机器学习实战源码/Ch02/datingTestSet2.txt")
    # normMat, ranges, minValues = autoNorm(datingDataMat)
    # print(normMat)
    #print(datingLabels)
    # fig = plt.figure()
    # ax = fig.add_subplot(111)
    # ax.scatter(datingDataMat[:,1], datingDataMat[:,2], 15.0*array(datingLabels), 15.0*array(datingLabels))
    # plt.show()

if __name__ == '__main__':
    main()
